﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if !WINDOWS_PHONE


#endif

namespace DARE
{
    [AttrTypeDependentSerializable(typeof(DARE.Save.DataStructures.CCameraMgrData))]
    public class CCameraMgr
    {

    #region fields

        private int m_activeCameraIndex = -1;
        private CCamera m_activeCamera = null;
        private Dictionary<string, CCamera> m_gameCameras = new Dictionary<string, CCamera>();
        private Dictionary<string, CCamera> m_renderCameras = new Dictionary<string, CCamera>();

    #endregion

    #region properties

        /// <summary>
        /// The active camera is the one throught the scene is rendered.
        /// Get the active camera.
        /// Set the active camera.
        /// </summary>
        public CCamera ActiveCamera
        {
            get { return m_activeCamera; }
            set { SetActiveCamera(value); }
        }

        /// <summary>
        /// Get the active camera index.
        /// Set the active camera by its index.
        /// </summary>
        public int ActiveCameraIndex
        {
            get { return m_activeCameraIndex; }
            set { SetActiveCamera(value); }
        }

        /// <summary>
        /// Get the number of cameras in the CameraManager.
        /// </summary>
        public int Count
        {
            get { return m_gameCameras.Count; }
        }

        /// <summary>
        /// Get the render cameras. Render cameras are used to draw shadows / mirrors... and render 
        /// effects.
        /// </summary>
        public Dictionary<string, CCamera> RenderCameras
        {
            get { return m_renderCameras; }
        }

    #endregion

    #region ctor

        /// <summary>
        /// Create the camera manager.
        /// </summary>
        public CCameraMgr()
        {
            Initialize();
        }

        /// <summary>
        /// Initialize the camera manager.
        /// </summary>
        private void Initialize()
        {
            CCamera shadowCamera = new CFirstPersonCamera("ShadowsCamera");
            shadowCamera.SetPerspectiveFov(MathHelper.PiOver4, 1, 0.5f, 500);
            m_renderCameras.Add(shadowCamera.Name, shadowCamera);
        }

    #endregion

    #region methods

        /// <summary>
        /// Get the list of cameras in the camera manager.
        /// </summary>
        /// <returns>List of cameras.</returns>
        public List<CCamera> GetGameCamerasList()
        {
            List<CCamera> cams = new List<CCamera>();
            foreach (CCamera cam in m_gameCameras.Values)
                cams.Add(cam);
            return cams;
        }

        public List<CCamera> GetRenderingCameraList()
        {
            List<CCamera> cams = new List<CCamera>();
            foreach (CCamera cam in m_gameCameras.Values)
                if (cam.IsRendering)
                    cams.Add(cam);
            return cams;
        }

        /// <summary>
        /// Update the active camera if there is one.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            foreach (CCamera cam in m_gameCameras.Values)
                if (cam.IsRendering)
                    cam.Update(gameTime);
        }

        /// <summary>
        /// Register a camera to the camera manager.
        /// The first camera added to the camara manager will be set to active.
        /// </summary>
        /// <param name="camera">Camera to register.</param>
        public void RegisterCamera(CCamera camera)
        {
            if (m_gameCameras.ContainsKey(camera.Name))
                throw new Exception("A camera with name " + camera.Name + " already exists !");
            m_gameCameras.Add(camera.Name, camera);
            if (m_activeCameraIndex == -1)
            {
                m_activeCamera = camera;
                camera.IsActive = true;
                m_activeCameraIndex = 0;
            }
        }

        /// <summary>
        /// Unregister a camera by his name.
        /// The camera will be removed from the camera manager and then will not 
        /// be able to be active.
        /// </summary>
        /// <param name="name">Camera's name.</param>
        public void UnregisterCamera(string name)
        {
            if (m_gameCameras.ContainsKey(name))
            {
                m_gameCameras[name].IsActive = false;
                m_gameCameras.Remove(name);
                if (!m_gameCameras.ContainsValue(m_activeCamera))
                {
                    m_activeCamera = m_gameCameras.First().Value;
                    m_activeCameraIndex = 0;
                }
                if (m_gameCameras.Count == 0)
                    m_activeCameraIndex = -1;
            }
        }

        /// <summary>
        /// Unregister a camera.
        /// The camera will be removed from the camera manager and 
        /// then will not be able to be active.
        /// </summary>
        /// <param name="camera">Camera to unregister.</param>
        public void UnregisterCamera(CCamera camera)
        {
            UnregisterCamera(camera.Name);
        }

        /// <summary>
        /// Unregister all the cameras in the camera manager.
        /// </summary>
        public void UnregisterCameras()
        {
            m_gameCameras.Clear();
            m_activeCamera = null;
            m_activeCameraIndex = -1;
        }

        /// <summary>
        /// Same as UnregisterCameras.
        /// </summary>
        public void Clear()
        {
            m_gameCameras.Clear();
            m_activeCamera = null;
            m_activeCameraIndex = -1;
        }

        /// <summary>
        /// Get a camera by its index. Null is return if the camera does not exist.
        /// </summary>
        /// <param name="index">Index of the camera.</param>
        /// <returns>The camera.</returns>
        public CCamera GetCamera(int index)
        {
            if ((index < m_gameCameras.Count) && (index >= 0))
                return m_gameCameras.ElementAt(index).Value;
            return null;
        }

        /// <summary>
        /// Get a camera by his name. Null is return if the camera does not exist.
        /// </summary>
        /// <param name="name">Name of the camera.</param>
        /// <returns>The camera.</returns>
        public CCamera GetCamera(string name)
        {
            if (m_gameCameras.ContainsKey(name))
                return m_gameCameras[name];
            return null;
        }

        /// <summary>
        /// The active camera is the one throught the scene is rendered.
        /// Set the active camera by its index. If no camera is found, null is returned and 
        /// no camera is set to active.
        /// </summary>
        /// <param name="index">Index of the camera.</param>
        /// <returns>The camera.</returns>
        public CCamera SetActiveCamera(int index)
        {
            m_activeCamera = GetCamera(index);
            if (m_activeCamera != null)
                m_activeCameraIndex = index;
            else
                m_activeCameraIndex = -1;
            foreach (CCamera camera in m_gameCameras.Values)
                camera.IsActive = false;
            if (m_activeCamera != null)
                 m_activeCamera.IsActive = true;
            return m_activeCamera;
        }

        /// <summary>
        /// The active camera is the one throught the scene is rendered.
        /// Set the active camera by its name. If no camera is found, null is returned and 
        /// no camera is set to active.
        /// </summary>
        /// <param name="name">Name of the camera.</param>
        /// <returns>The camera.</returns>
        public CCamera SetActiveCamera(string name)
        {
            m_activeCamera = GetCamera(name);
            if (m_activeCamera != null)
            {
                int index = 0;

                foreach (string cam in m_gameCameras.Keys)
                {
                    if (cam == name)
                        break;
                    index += 1;
                }
                m_activeCameraIndex = index;
            }
            else
                m_activeCameraIndex = -1;
            foreach (CCamera camera in m_gameCameras.Values)
                camera.IsActive = false;
            if (m_activeCamera != null)
                m_activeCamera.IsActive = true;
            else
                m_activeCamera = m_renderCameras[name];
            return m_activeCamera;
        }

        /// <summary>
        /// The active camera is the one throught the scene is rendered.
        /// Set the active camera by its name. if no camera is found, null is returned and 
        /// no camera is set to active.
        /// </summary>
        /// <param name="camera">Camera to set active.</param>
        /// <returns>The camera.</returns>
        public CCamera SetActiveCamera(CCamera camera)
        {
            return SetActiveCamera(camera.Name);
        }

    #endregion

    }
}
